/* Includes ------------------------------------------------------------------*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "nat.h"

/* typedef -------------------------------------------------------------------*/
/* define --------------------------------------------------------------------*/
/* macro ---------------------------------------------------------------------*/
/* variables -----------------------------------------------------------------*/
#ifdef NAT_AT_STATIC_BUFFER
static uint8_t bufferAT[NAT_AT_BUFFER_SIZE] = {0};
#endif
/* function prototypes -------------------------------------------------------*/
extern void NEDelay_Ms(int count);
/* functions -----------------------------------------------------------------*/
int Nat_RxPacketFliter(struct NatAdapter *adapter, struct Nat *at, char *buffer, size_t size);

/*********************************************************************************************

  * @brief  Nat_ExecuteCommand
  * @param  *nat: at object pointer
  *         *waitTime: the interval of waiting reponse , unit is millise second
  * 		*reponse, wait until match response
  *         *packet: the packet of data which need send to device
  *			szie: the byte count of packet
  * @return 0: get response message, put message into response pointer
  *         -62: wait time out
  * @remark 

  ********************************************************************************************/
int Nat_SendRaw(struct NatAdapter *adapter, struct Nat *at, int32_t waitTime, char *response, uint8_t *packet, size_t size)
{
	int result = -62, countRx, countTx = 0;
	
	/* alloc memory for receive buffer */
#ifndef NAT_AT_STATIC_BUFFER
	char *bufferAT = malloc(NAT_AT_BUFFER_SIZE);
	if(bufferAT == NULL)
	{   
		result = -105;		// ENOBUFS
		goto __end;
	}
#endif
	memset(bufferAT, 0, NAT_AT_BUFFER_SIZE);
	
	adapter->write(packet, size);

    if(waitTime == -1)
    {   
		result = 0;
		goto __end;
	}

    /* if get the message exclude urcs in specified interval, 
       then copy it to response and return successfully */
	adapter->response.counter = 0;
	memset(bufferAT, 0, NAT_AT_BUFFER_SIZE);
	for(int i=-1; i<(waitTime/100); i++)
    {
		NEDelay_Ms(100);
        if((countRx = adapter->read(bufferAT, NAT_AT_BUFFER_SIZE)) > 0)
        {
			if(Nat_RxPacketFliter(adapter, at, bufferAT, countRx) == 0)
			{	
				
				if(response != NULL
				   && Nat_ResponseCheck(&adapter->response, response) != NULL)
				{	
					result = 0;
					break;	
				}
			}
			memset(bufferAT, 0, NAT_AT_BUFFER_SIZE);
		}
		
		if(waitTime == 0)
		{	break;	}
    }

__end:
#ifndef NAT_AT_STATIC_BUFFER
	if(bufferAT != NULL)
    {   free(bufferAT);  }
#endif
    return result;
}

/*********************************************************************************************

  * @brief  Nat_ExecuteCommand
  * @param  *nat: at object pointer
  *         *waitTime: the interval of waiting reponse , unit is millise second'
  * 		*reponse, wait until match response
  *         *format, the format of at, dont need \r\n, the function will fill "\r\n" at the end of command
  *			args: args for fill string format
  * @return 0: get response message, put message into response pointer
  *			-105: no space to alloc
  *         -62: wait time out
  * @remark 

  ********************************************************************************************/
int _Nat_ExecuteCommand(struct NatAdapter *adapter, struct Nat *at, int32_t waitTime, char *response, char *format, va_list args)
{
    int result = -62, countRx, countTx = 0;
	
#ifndef NAT_AT_STATIC_BUFFER
	/* alloc memory for receive buffer */
	char *bufferAT = malloc(NAT_AT_BUFFER_SIZE);
	if(bufferAT == NULL)
	{   
		result = -105;		// ENOBUFS
		goto __end;
	}
#endif
	memset(bufferAT, 0, NAT_AT_BUFFER_SIZE);
	
	
	if(format != NULL)
	{
		/* get args combine command string and send it to module */
		vsnprintf(bufferAT, NAT_AT_BUFFER_SIZE, format, args);
		countTx = strlen(bufferAT);
		adapter->write(bufferAT, countTx);
		if(bufferAT[countTx - 2] != '\r' && bufferAT[countTx - 1] != '\n')
		{	adapter->write("\r\n", 2);	}
	}

    if(waitTime == -1)
    {   
		result = 0;
		goto __end;
	}
    

    /* if get the message exclude urcs in specified interval, 
       then copy it to response and return successfully */
	adapter->response.counter = 0;
	memset(bufferAT, 0, NAT_AT_BUFFER_SIZE);
	for(int i=-1; i<(waitTime/100); i++)
    {
		NEDelay_Ms(100);
        if((countRx = adapter->read(bufferAT, NAT_AT_BUFFER_SIZE)) > 0)
        {
			if(Nat_RxPacketFliter(adapter, at, bufferAT, countRx) == 0)
			{	
				if(response != NULL
				   && Nat_ResponseCheck(&adapter->response, response) != NULL)
				{	
					result = 0;	
					break;
				}
			}
			
			memset(bufferAT, 0, NAT_AT_BUFFER_SIZE);
		}
		
		if(waitTime == 0)
		{	break;	}
    }

__end:
#ifndef NAT_AT_STATIC_BUFFER
    if(bufferAT != NULL)
    {   free(bufferAT);  }
#endif
    return result;
}
int Nat_ExecuteCommand(struct NatAdapter *adapter, struct Nat *at, int32_t waitTime, char *response, char *format, ...)
{
	int result = 0;
	
	va_list args;
    va_start(args, format);
	result = _Nat_ExecuteCommand(adapter, at, waitTime, response, format, args);
	va_end(args);

	return result;
}
/*********************************************************************************************

  * @brief  Nat_RxPacketFliter
  * @param  *adapter, at driver adapter
  *         *nat: at object pointer
  *         *buffer, rx packet pointer
  *         *size, rx packet length(bytes)
  * @return 
  * @remark use this function to scan every line of the whole packet to find urc message 
  *         the remain message is the response message, put it into response of at 

  ********************************************************************************************/
int Nat_RxPacketFliter(struct NatAdapter *adapter, struct Nat *at, char *buffer, size_t size)
{     
    int seekLineStart = 0, result = -2;
    struct NatUrc *urc = NULL;
	bool isMatch = false, isLineChecked = false;
	 
    for(int seek=0; seek<size; )
    {
		isMatch = false;
        if(strncmp("\r\n", buffer + seek, 2) == 0)
        {   
			/* put the line which not match urc into response buffer list */
			if(isLineChecked == false
				&& seek > seekLineStart
			    && adapter->response.counter < RIL_AT_RESPONSE_COUNT
				&& (seek - seekLineStart) < sizeof(adapter->response.buf[0]))
			{				
				memset(adapter->response.buf[adapter->response.counter], 0, sizeof(adapter->response.buf[adapter->response.counter]));
				memcpy(adapter->response.buf[adapter->response.counter], 
					   buffer + seekLineStart,
					   seek - seekLineStart);
				adapter->response.counter += 1;
				result = 0;
			}
			
            seek += 2;  
            seekLineStart = seek;
			isLineChecked = false;
            continue;
        }

        /* traverse urc list, find urc message by urc prefix */
        urc = (struct NatUrc *)at->urcs;
        do
        {
            if(strncmp(buffer + seek, urc->prefix, strlen(urc->prefix)) == 0)   
            {
                seek += urc->handle(buffer + seekLineStart);
				isMatch = true;
				isLineChecked = true;
                break;
            }
            urc += 1;
        } while (urc->prefix != NULL);
		if(isMatch == true)
		{	continue;	}
        		
		seek += 1;
    }
	
	/* the list line, fill into response buffer list  */
	if(isLineChecked == false
		&& seekLineStart != size
		&& adapter->response.counter < RIL_AT_RESPONSE_COUNT
		&& (size - seekLineStart) < sizeof(adapter->response.buf[0]))
	{
		memset(adapter->response.buf[adapter->response.counter], 0, sizeof(adapter->response.buf[adapter->response.counter]));
		memcpy(adapter->response.buf[adapter->response.counter], 
			   buffer + seekLineStart,
			   size - seekLineStart);
		adapter->response.counter += 1;
		result = 0;
	}

    return result;
}
/*********************************************************************************************

  * @brief  Nat_ResponseCheck
  * @param  *response, at response obj pointer
			*matchString, string which wanted to match
  * @return the response string pointer
  * @remark loop response list, find response which match the arg matchString. return the match response

  ********************************************************************************************/
char* Nat_ResponseCheck(struct NatResponse *response, char *matchString)
{
	for(int i=0; i<response->counter; i++)
	{
		if(strstr(response->buf[i], matchString) != 0)
		{	return response->buf[i];	}
	}
	
	return NULL;
}
